<?php
namespace SwoStar\Container;

use Closure;
use Exception;

/**
 * 容器
 */
class Container
{
    // 0. 单例
    protected static $instance;
    
    // 1. 容器，用于记录创建好的对象信息或者数据
    protected $bindings = [];

    // 1. 容器，用于记录创建好的对象信息或者数据
    protected $instances = [];

    /**
     * 容器绑定的方法 : 就是把一个对象存储进数组中
     * @param  string $abstract 标识
     * @param  object $object   实例对象或者闭包
     */
    public function bind($abstract, $object)
    {
        // 标识要绑定
        // 1. 就是一个对象
        // 2. 闭包的方式
        // 3. 类对象的字符串 (类的地址)
        $this->bindings[$abstract] = $object;
    }

    /**
     * 从容器中解析实例对象或者闭包
     * @param  string $abstract   标识
     * @param  array  $parameters 传递的参数
     * @return object             是一个闭包或者对象
     */
    public function make($abstract, $parameters = [])
    {
        //如果已经记录了对象，则返回对象
        //参数如：config
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        //传递的是命名空间，则创建对象
        //参数如：SwoStar\Consul\Response
        if (!$this->has($abstract)) {

            // 如果不存在自行
            // 选择返回, 可以抛出一个异常
            // throw new Exception('没有找到这个容器对象'.$abstract, 500);

            $object = $abstract;
        } else {
            //获取bind()中绑定好的对象或者属性信息
            $object = $this->bindings[$abstract];
        }

        // 在这个容器中是否存在
        // 1. 判断是否一个为对象
        // 2. 闭包的方式
        if ($object instanceof Closure) {
            return $object();
        }

        // 3. 类对象的字符串 (类的地址)
        //创建对象并且记录数组中
        return $this->instances[$abstract] = (is_object($object)) ? $object :  new $object(...$parameters) ;
    }

    /**
     * 判断是否在容器中
     * 容器很多便于扩展
     * 可能在其他场景中会用到
     * @param $abstract
     * @return bool
     */
    public function has($abstract)
    {
        return isset($this->bindings[$abstract]);
    }

    /**
     * 单例创建
     * @return mixed
     */
    public static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }

        return static::$instance;
    }

    /**
     * @param null $container
     * @return null
     */
    public static function setInstance($container = null)
    {
        return static::$instance = $container;
    }
}
